#pragma once
#include<iostream>
#include<pthread.h>
#include<vector>
#include<assert.h>
#include<semaphore.h>

static const int gcap = 2;

template <class T>
class Ringqueue
{
private:
    void P(sem_t& sem)
    {
        int n = sem_wait(&sem);
        assert(n==0);
        (void)n;
    }

    void V(sem_t& sem)
    {
        int n = sem_post(&sem);
        assert(n==0);
        (void)n;
    }

public:
    Ringqueue(const int& cap = gcap):_cap(cap),_queue(cap)
    {
        int n = sem_init(&_dataSem,0,0);
        assert(n==0);
        n = sem_init(&_spaceSem,0,_cap);
        assert(n==0);

        _datastep = _spacestep = 0;

        pthread_mutex_init(&_pmutex,nullptr);
        pthread_mutex_init(&_cmutex,nullptr);
    }

    void push(const T& in)
    {
        P(_spaceSem);
        pthread_mutex_lock(&_pmutex);
        _queue[_spacestep++] = in;
        _spacestep %= _cap;
        pthread_mutex_unlock(&_pmutex);
        V(_dataSem);

    }

    void pop(T *out)
    {
        P(_dataSem);
        pthread_mutex_lock(&_cmutex);
        *out = _queue[_datastep++];
        _datastep %= _cap;
        pthread_mutex_unlock(&_cmutex);
        V(_spaceSem);
    }

    ~Ringqueue()
    {
        sem_destroy(&_dataSem);
        sem_destroy(&_spaceSem);

        pthread_mutex_destroy(&_pmutex);
        pthread_mutex_destroy(&_cmutex);
    }

private:
    int _cap;
    std::vector<T> _queue;
    sem_t _dataSem;
    sem_t _spaceSem;
    pthread_mutex_t _pmutex;
    pthread_mutex_t _cmutex;
    int _datastep;
    int _spacestep;
};